/* 
 * Copyright (c) 2012 JSquirrelEngine
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in 
 * the Software without restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
 * Software, and to permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * 
 */

package org.jsquirrel.geometry.impl;

import org.jsquirrel.geometry.BoundingArea;
import org.jsquirrel.geometry.BoundingAreaType;
import org.jsquirrel.geometry.Polygon;
import org.jsquirrel.geometry.UnsupportedBoundingIntersectionException;
import org.jsquirrel.geometry.VertexIterator;
import org.jsquirrel.math.F2Vector;
import org.jsquirrel.math.FInterval;


/**
 * Implementation of class BoundingArea, the bounding rectangle is the minimum rectangle
 * that completely contains a Polygon.
 * 
 * @author Juan David Adarve Bermudez
 *
 */
public class BoundingRectangle extends BoundingArea {
	
	//*********************************************************************************************
	// INSTANCE ATTRIBUTES
	//*********************************************************************************************
	
	/**
	 * position of the bottom left corner of the rectangle
	 */
	protected float xLeft, yLeft;
	
	/**
	 * position of the upper right corner
	 */
	protected float xRight, yRight;
	
	//*********************************************************************************************
	// CONSTRUCTORS
	//*********************************************************************************************
	
	public BoundingRectangle() {
		super(BoundingAreaType.BOUNDING_RECTANGLE);
		xLeft = 0;
		yLeft = 0;
		xRight = 0;
		yRight = 0;
	}
	
	//*********************************************************************************************
	// PUBLIC METHODS
	//*********************************************************************************************
	@Override
	public boolean intersects(BoundingArea area) throws UnsupportedBoundingIntersectionException {
		
		switch(area.type()) {
			case BOUNDING_RECTANGLE:
				return rectangleIntersection((BoundingRectangle)area);
				
			case BOUNDING_CIRCLE:
				return circleIntersection((BoundingCircle)area);
		}
		
		throw new UnsupportedBoundingIntersectionException("BoundingRectangle.intersects(): Unknown BoundingArea type: " + area.type());
	}
	
	@Override
	public boolean intersectsRectangle(float xLeft, float yLeft, float width,
			float height) throws UnsupportedBoundingIntersectionException {
		
		return FInterval.intersection(this.xLeft, this.xRight, xLeft, xLeft + width) &&
		FInterval.intersection(this.yLeft, this.yRight, yLeft, yLeft + height);
	}
	
	
	@Override
	public void getBoundingRectangle(F2Vector bLeftCorner, F2Vector uRightCorner) {
		
		bLeftCorner.setX(xLeft);
		bLeftCorner.setY(yLeft);
		
		uRightCorner.setX(xRight);
		uRightCorner.setY(yRight);
	}
	
	@Override
	public void update(Polygon p) {
		
		
		if(p.length() > 0) {
			VertexIterator it = p.getIterator();
						
			F2Vector vertex = it.next();
			
			xLeft = vertex.x;
			yLeft = vertex.y;
			
			xRight = vertex.x;
			yRight = vertex.y;
			
			while(it.hasNext()) {
				
				vertex = it.next();
				float x = vertex.x;
				float y = vertex.y;
				
				xLeft = x < xLeft? x : xLeft;
				yLeft = y < yLeft? y : yLeft;
				
				xRight = x > xRight? x : xRight;
				yRight = y > yRight? y : yRight;
			}
			
		}
		
	}
	
	@Override
	public String toString() {
		return "br={(" + xLeft + ", " + yLeft + "), (" + xRight + ", " + yRight + ")}";
	}
	
	/**
	 * 
	 * @return X coordinate of the bottom left corner.
	 */
	public float xLeft() {
		return xLeft;
	}
	
	/**
	 * 
	 * @return Y coordinate of the bottom left corner.
	 */
	public float yLeft() {
		return yLeft;
	}
	
	/**
	 * 
	 * @return X coordinate of the upper right corner.
	 */
	public float xRight() {
		return xRight;
	}
	
	/**
	 * 
	 * @return Y coordinate of the upper right corner.
	 */
	public float yRight() {
		return yRight;
	}
	
	
	//*********************************************************************************************
	// PRIVATE METHODS
	//*********************************************************************************************
	
	private boolean rectangleIntersection(BoundingRectangle rectangle) {
		return FInterval.intersection(xLeft, xRight, rectangle.xLeft, rectangle.xRight) &&
		FInterval.intersection(yLeft, yRight, rectangle.yLeft, rectangle.yRight);
	}
	
	
	private boolean circleIntersection(BoundingCircle circle) {
		
		// half the width and height of the rectangle
		float wh, hh;
		wh = (xRight - xLeft)/2;
		hh = (yRight - yLeft)/2;
		
		// coordinates for the center of the rectangle
		float xc, yc;
		xc = xLeft + wh;
		yc = yLeft + hh;
		
		
		/*
		 * NOTE: By including the two comparison of the center coordinates with the two X and Y axis,
		 * the execution time for the intersection when it is inside the Voronoi Area is constant
		 * with some overhead, though. 
		 */		
		// if the center of the circle is inside the Voronoi Area of the rectangle
		if(FInterval.pointInside(xLeft, xRight, circle.center.x()) ||
				FInterval.pointInside(yLeft, yRight, circle.center.y())) {
		
			return Math.abs(circle.center.x() - xc) <= circle.radius + wh ||
				Math.abs(circle.center.y() - yc) <= circle.radius + hh;
			
		}
		// outside of the Boronoy Area...
		else {
			
			// distance from the center to one corner of the rectangle
			float hyp = (float)Math.sqrt(wh*wh + hh*hh);
			
			return F2Vector.distanceBetween(xc, yc, circle.center.x(), circle.center.y()) <= circle.radius + hyp;
		}
	}

	

	
}